package panda.lang;

import java.nio.CharBuffer;


/**
 * @author yf.frank.wang@gmail.com
 */
public abstract class CharSequences {
	// -----------------------------------------------------------------------
	/**
	 * <p>
	 * Returns a new {@code CharSequence} that is a subsequence of this sequence starting with the
	 * {@code char} value at the specified index.
	 * </p>
	 * <p>
	 * This provides the {@code CharSequence} equivalent to {@link String#substring(int)}. The
	 * length (in {@code char}) of the returned sequence is {@code length() - start}, so if
	 * {@code start == end} then an empty sequence is returned.
	 * </p>
	 * 
	 * @param cs the specified subsequence, null returns null
	 * @param start the start index, inclusive, valid
	 * @return a new subsequence, may be null
	 * @throws IndexOutOfBoundsException if {@code start} is negative or if {@code start} is greater
	 *             than {@code length()}
	 */
	public static CharSequence subSequence(CharSequence cs, int start) {
		return cs == null ? null : cs.subSequence(start, cs.length());
	}

	// -----------------------------------------------------------------------
	/**
	 * <p>
	 * Finds the first index in the {@code CharSequence} that matches the specified character.
	 * </p>
	 * 
	 * @param cs the {@code CharSequence} to be processed, not null
	 * @param searchChar the char to be searched for
	 * @param start the start index, negative starts at the string start
	 * @return the index where the search char was found, -1 if not found
	 */
	public static int indexOf(CharSequence cs, int searchChar, int start) {
		if (cs instanceof String) {
			return ((String)cs).indexOf(searchChar, start);
		}
		else {
			int sz = cs.length();
			if (start < 0) {
				start = 0;
			}
			for (int i = start; i < sz; i++) {
				if (cs.charAt(i) == searchChar) {
					return i;
				}
			}
			return -1;
		}
	}

	/**
	 * Used by the indexOf(CharSequence methods) as a green implementation of indexOf.
	 * 
	 * @param cs the {@code CharSequence} to be processed
	 * @param searchChar the {@code CharSequence} to be searched for
	 * @param start the start index
	 * @return the index where the search sequence was found
	 */
	public static int indexOf(CharSequence cs, CharSequence searchChar, int start) {
		return cs.toString().indexOf(searchChar.toString(), start);
		// if (cs instanceof String && searchChar instanceof String) {
		// // TODO: Do we assume searchChar is usually relatively small;
		// // If so then calling toString() on it is better than reverting to
		// // the green implementation in the else block
		// return ((String) cs).indexOf((String) searchChar, start);
		// } else {
		// // TODO: Implement rather than convert to String
		// return cs.toString().indexOf(searchChar.toString(), start);
		// }
	}

	/**
	 * <p>
	 * Finds the last index in the {@code CharSequence} that matches the specified character.
	 * </p>
	 * 
	 * @param cs the {@code CharSequence} to be processed
	 * @param searchChar the char to be searched for
	 * @param start the start index, negative returns -1, beyond length starts at end
	 * @return the index where the search char was found, -1 if not found
	 */
	public static int lastIndexOf(CharSequence cs, int searchChar, int start) {
		if (cs instanceof String) {
			return ((String)cs).lastIndexOf(searchChar, start);
		}
		else {
			int sz = cs.length();
			if (start < 0) {
				return -1;
			}
			if (start >= sz) {
				start = sz - 1;
			}
			for (int i = start; i >= 0; --i) {
				if (cs.charAt(i) == searchChar) {
					return i;
				}
			}
			return -1;
		}
	}

	/**
	 * Used by the lastIndexOf(CharSequence methods) as a green implementation of lastIndexOf
	 * 
	 * @param cs the {@code CharSequence} to be processed
	 * @param searchChar the {@code CharSequence} to be searched for
	 * @param start the start index
	 * @return the index where the search sequence was found
	 */
	public static int lastIndexOf(CharSequence cs, CharSequence searchChar, int start) {
		return cs.toString().lastIndexOf(searchChar.toString(), start);
		// if (cs instanceof String && searchChar instanceof String) {
		// // TODO: Do we assume searchChar is usually relatively small;
		// // If so then calling toString() on it is better than reverting to
		// // the green implementation in the else block
		// return ((String) cs).lastIndexOf((String) searchChar, start);
		// } else {
		// // TODO: Implement rather than convert to String
		// return cs.toString().lastIndexOf(searchChar.toString(), start);
		// }
	}

	public static CharSequence toCharSequence(char[] array) {
		return CharBuffer.wrap(array);
	}
	
	public static CharSequence toCharSequence(char[] array, int start, int length) {
		return CharBuffer.wrap(array, start, length);
	}
	
	/**
	 * Green implementation of toCharArray.
	 * 
	 * @param cs the {@code CharSequence} to be processed
	 * @return the resulting char array
	 */
	public static char[] toCharArray(CharSequence cs) {
		if (cs instanceof String) {
			return ((String)cs).toCharArray();
		}
		else {
			int sz = cs.length();
			char[] array = new char[cs.length()];
			for (int i = 0; i < sz; i++) {
				array[i] = cs.charAt(i);
			}
			return array;
		}
	}

	/**
	 * Green implementation of regionMatches.
	 * 
	 * @param cs the {@code CharSequence} to be processed
	 * @param ignoreCase whether or not to be case insensitive
	 * @param thisStart the index to start on the {@code cs} CharSequence
	 * @param substring the {@code CharSequence} to be looked for
	 * @param start the index to start on the {@code substring} CharSequence
	 * @param length character length of the region
	 * @return whether the region matched
	 */
	public static boolean regionMatches(CharSequence cs, boolean ignoreCase, int thisStart,
			CharSequence substring, int start, int length) {
		if (cs instanceof String && substring instanceof String) {
			return ((String)cs).regionMatches(ignoreCase, thisStart, (String)substring, start,
				length);
		}
		else {
			// TODO: Implement rather than convert to String
			return cs.toString().regionMatches(ignoreCase, thisStart, substring.toString(), start,
				length);
		}
	}
}
